package keeto.com.vn.classified.core;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import keeto.com.vn.classfied.model.Command;
import keeto.com.vn.classified.config.IConfig;
import keeto.com.vn.classified.config.IConstant;
import keeto.com.vn.classified.database.BuiltinService;
import keeto.com.vn.classified.database.BuiltinServiceManager;
import keeto.com.vn.classified.database.DBString;
import keeto.com.vn.classified.manager.IAManager;
import keeto.com.vn.classified.network.IProtocol;
import keeto.com.vn.classified.network.JSONProtocol;
import keeto.com.vn.classified.task.TaskChangeAvatar;
import keeto.com.vn.classified.task.TaskChangePass;
import keeto.com.vn.classified.task.TaskComment;
import keeto.com.vn.classified.task.TaskCommentProduct;
import keeto.com.vn.classified.task.TaskForgotPass;
import keeto.com.vn.classified.task.TaskGetAndroidInterestShop;
import keeto.com.vn.classified.task.TaskGetAndroidProductProposed;
import keeto.com.vn.classified.task.TaskGetAndroidProductSameCost;
import keeto.com.vn.classified.task.TaskGetAndroidProductType;
import keeto.com.vn.classified.task.TaskGetMyHandBook;
import keeto.com.vn.classified.task.TaskGetProductAd;
import keeto.com.vn.classified.task.TaskGetProductBySeller;
import keeto.com.vn.classified.task.TaskGetProductSubCate;
import keeto.com.vn.classified.task.TaskGetProfileInfo;
import keeto.com.vn.classified.task.TaskLogon;
import keeto.com.vn.classified.task.TaskLogout;
import keeto.com.vn.classified.task.TaskPublish;
import keeto.com.vn.classified.task.TaskRegister;
import keeto.com.vn.classified.task.TaskSendAndroidInterestShop;
import keeto.com.vn.classified.task.TaskSendAndroidProductAbusel;
import keeto.com.vn.classified.task.TaskUpdateUserInfo;
import keeto.com.vn.classified.task.TaskUpdateViewProduct;
import keeto.com.vn.classified.task.taskGetSearchGlobalProduct;
import keeto.com.vn.classified.ui.MainApplication;

import org.json.JSONObject;


import android.content.Context;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.State;
import android.os.Process;
import android.util.Log;
public class ClassifiedCore implements IConstant, Runnable, IConfig , IClassifiedCore {


	public static final int PRIORITY_BACKGROUND = 1;
	public static final int PRIORITY_LOW = 2;
	public static final int PRIORITY_MEDIUM = 3;
	public static final int PRIORITY_HIGH = 4;
	public static final int PRIORITY_TOP = 5;
	private static final String TAG = "KeetoClassifiedCore";

	private static ClassifiedCore mCore = null;

	private static Set<ClassifiedListener> mListeners = new CopyOnWriteArraySet<ClassifiedListener>();
	private BlockingQueue<Command> mCommands = new PriorityBlockingQueue<Command>();
	private volatile Boolean additionStatus = new Boolean(false);
	private volatile boolean ackLogon = false;

	private String authKey = UNINITIALIZED_STRING;
	private Context mContext;
	private Thread mThread;
	private IProtocol protocol;
	private SharedPreferences sp;
	private IAManager manager;
	private BuiltinServiceManager serviceManager;

	private int isLogon = IConstant.LOGON_NO;
	private boolean canLogon = false;
	private static boolean networkFailed = false;
	private boolean connected = true;
	private boolean isRunOnBackground = false;
	public int sleepTime = 10 * 1000;
	private ConnectionChangeReceiver ccReceiver;
	private byte[] connectionLock = new byte[0];
	boolean coreFlag = false;
	private boolean mHeartBeatInQueue = false;
	public static boolean sessionDuplicate = false;
	public ClassifiedCore(Context context) {
		protocol = new JSONProtocol(mContext, this);
		this.mContext = context;
		mThread = new Thread(this);
		mThread.setName("PushCore");
		mThread.start();
		serviceManager = BuiltinServiceManager.getInstance(context);
		manager = IAManager.getInstance(mContext);
		sp = context.getSharedPreferences(DBString.SP_NAME,Context.MODE_PRIVATE);
		String authkey = serviceManager.getBuitinService() ==  null ? "" : serviceManager.getBuitinService().getAuthKey();
		MainApplication.setAuthenKey(authkey);
	}

	public void setLogon(int isLogon) {
		this.isLogon = isLogon;
	}

	private void setAuthenKey() {
		Editor editor = sp.edit();
		editor.remove(DBString.SP_AUTHKEY);
		editor.commit();
	}

	private void cleanAuthenKey() {
		Editor editor = sp.edit();
		editor.remove(DBString.SP_AUTHKEY);
		editor.commit();
	}

	private void cleanAckLogon() {
		Editor editor = sp.edit();
		editor.remove(DBString.SP_ACK_LOGON);
		editor.commit();
	}

	public static Set<ClassifiedListener> getAllListeners(
			ClassifiedListener listener) {
		if (listener == null) {
			return mListeners;
		}

		Set<ClassifiedListener> listeners = new HashSet<ClassifiedListener>(
				mListeners);
		listeners.add(listener);
		return listeners;
	}

	@Override
	public void setCanLogon(boolean canLogon) {
		this.canLogon = canLogon;
	}

	@Override
	public boolean isCanLogon() {
		return canLogon;
	}


	public int getIsLogon() {
		return isLogon;
	}

	public void setIsLogon(int isLogon) {
		this.isLogon = isLogon;
	}
	public byte[] getConnectionLock() {
		return connectionLock;
	}

	public boolean isWifiConnected() {
		ConnectivityManager conMan = (ConnectivityManager) mContext
				.getSystemService(Context.CONNECTIVITY_SERVICE);

		State wifi = conMan.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();

		if (wifi == State.CONNECTED)
			return true;
		else
			return false;

	}


	public void registerReceiver() {
		IntentFilter filter = new IntentFilter(
				ConnectivityManager.CONNECTIVITY_ACTION);
		ccReceiver = new ConnectionChangeReceiver(this);
		mContext.registerReceiver(ccReceiver, filter);
	}

	public void unregisterReceiver() {
		mContext.unregisterReceiver(ccReceiver);
	}

	public void addListener(ClassifiedListener listener) {
		if (listener != null) {
			mListeners.add(listener);
		}
	}

	public void removeListener(ClassifiedListener listener) {
		mListeners.remove(listener);
	}

	public synchronized static ClassifiedCore getInstance(Context context){
		if (mCore == null){
			mCore = new ClassifiedCore(context);
		}

		return mCore;

	}
	private void resetSleepTime() {
		sleepTime = 10 * 1000;
	}

	private int increaseSleepTime() {
		if (sleepTime == 10 * 1000) {
			// 30 seconds
			sleepTime = 30 * 1000;
		} else if (sleepTime == 30 * 1000) {
			// 3 minutes
			sleepTime = 3 * 60 * 1000;
		} else if (sleepTime == 3 * 60 * 1000) {
			// 5 minutes
			sleepTime = 5 * 60 * 1000;
		}
		return sleepTime;
	}



	@Override
	public void run() {
		// TODO Auto-generated method stub
		Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
		coreFlag = true;
		while (coreFlag) {

			String commandDescription = null;
			try {
				final Command command = mCommands.take();

				if (!connected) {
					if (command != null) {

						if (command.sendByUI) {
							command.runnable.timeoutCallback();
							continue;
						} else {
							mCommands.add(command);
						}
					}

					// hold lock and waiting for ConnectionChangeReceiver's
					// notify
					synchronized (connectionLock) {
						// waiting for 20s before CONTINUE executing
						connectionLock.wait(NETWORK_FAIL_WAIT);
						continue;
					}
				}

				if (command != null) {
					commandDescription = command.action;
					try {
						// execute the command
						command.runnable.run();
						resetSleepTime();
					} catch (Exception e) {

						if (command.sendByUI) {
							command.runnable.timeoutCallback();
						} else {
							// default retryTime is 3, so each command can be
							// retried for 3 times.
							if (command.retryTime > 0) {
								command.retryTime--;

								// not thread safe?
								new Thread() {
									@Override
									public void run() {
										try {
											sleep(increaseSleepTime());
											mCommands.put(command);
										} catch (InterruptedException e) {
											e.printStackTrace();
										}
									}

								}.start();
							}
						}
						e.printStackTrace();
					}
					// callback execution complete
					for (ClassifiedListener l : copyAndReconstructListeners(command.listener)) {
						l.commandExecutionComplete(mCommands.size() > 0);
					}
				}
			}
			// prevent the Core thread from crash.
			catch (Exception e) {
				Log.e("DEBUG", "Error running command '" + commandDescription
						+ "'", e);
			}
		}

	}

	@Override
	public IProtocol getProtocol() {
		// TODO Auto-generated method stub
		return protocol;
	}

	@Override
	public Context getContext() {
		// TODO Auto-generated method stub
		return mContext;
	}

	@Override
	public String getAuthKey() {
		// TODO Auto-generated method stub
		return (authKey.equals(UNINITIALIZED_STRING)) ? sp.getString(
				DBString.SP_AUTHKEY, UNINITIALIZED_STRING) : authKey;
	}

	@Override
	public Set<ClassifiedListener> copyAndReconstructListeners(
			ClassifiedListener listener) {
		if (listener == null) {
			return mListeners;
		}

		Set<ClassifiedListener> listeners = new HashSet<ClassifiedListener>(mListeners);
		listeners.add(listener);
		return listeners;
	}

	@Override
	public boolean isNetworkFail() {
		// TODO Auto-generated method stub
		return networkFailed;
	}

	public static boolean isNetworkfail() {
		return networkFailed;
	}

	public void setRunOnBackground(boolean isRunOnBackground) {
		this.isRunOnBackground = isRunOnBackground;
	}

	public boolean isRunOnBackground() {
		return isRunOnBackground;
	}

	@Override
	public void setAuthKey(String authKey) {
		// TODO Auto-generated method stub
		this.authKey = authKey;
		Editor editor = sp.edit();
		editor.putString(DBString.SP_AUTHKEY, authKey);
		editor.commit();
		Log.d(TAG,
				"authkey: "
						+ sp.getString(DBString.SP_AUTHKEY,
								UNINITIALIZED_STRING));
	}
	@Override
	public void stopThreads() {
		// TODO Auto-generated method stub
		mCommands.clear();
		restartCore();
	}

	private void restartCore() {
		coreFlag = false;
		mThread = new Thread(this);
		mThread.start();
	}

	@Override
	public void stopCoreAndDestroy() {
		this.stopThreads();
		mThread.interrupt();
		mThread = null;
	}

	public void setConnected(boolean connected) {
		this.connected = connected;
	}

	public boolean isConnected() {
		return connected;
	}

	public static AtomicInteger sequencing = new AtomicInteger(0);
	public static boolean autologon;

	private void putCommand(BlockingQueue<Command> queue, String action,
			ClassifiedListener listener, MockRunnable runnable, int priority,
			boolean sendByUI, int retryTimes) {
		int retries = 10;
		Exception e = null;
		while (retries-- > 0) {
			try {

				if (action.contains("TaskHeartBeat")) {
					if (mHeartBeatInQueue)
						return;
					mHeartBeatInQueue = true;
				}
				Command command = new Command(runnable, listener, action,
						priority, sendByUI);
				queue.put(command);
				return;
			} catch (InterruptedException ie) {
				try {
					Thread.sleep(IConfig.PUT_COMMAND_RETRY_INTERVAL);
				} catch (InterruptedException ne) {
				}
				e = ie;
			}
		}
		throw new Error(e);
	}

	public void put(ClassifiedListener listener, String action, int priority,
			MockRunnable runnable, boolean sendByUI) {
		putCommand(mCommands, action, listener, runnable, priority, sendByUI,
				Command.DEFAULT_RETRY_TIMES);
	}

	@Override
	public void setAdditionStatus(Boolean additionStatus) {
		// TODO Auto-generated method stub
		this.additionStatus = additionStatus;
	}

	@Override
	public Boolean isAdditionStatus() {
		// TODO Auto-generated method stub
		return additionStatus;
	}

	@Override
	public boolean isAckLogon() {
		// TODO Auto-generated method stub
		return ackLogon;
	}

	@Override
	public int taskGetAndroidProductAd(ClassifiedListener listener,
			int pageLimit, String sortBy, int pageNumber,String Area) {
		// TODO Auto-generated method stub
		TaskGetProductAd task = new TaskGetProductAd(listener, mCore,
				pageLimit, sortBy, pageNumber, Area);
		put(listener, task.getClass().getName(),
				ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;
	}

	@Override
	public int taskLogon(ClassifiedListener listener, String username,
			String password, String present) {
		TaskLogon task = new TaskLogon(listener, mCore, username, password,
				present);
		put(listener, task.getClass().getName(),ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;
	}

	@Override
	public void setAckLogon(boolean ackLogon) {
		// TODO Auto-generated method stub
		this.ackLogon = ackLogon;
		Editor editor = sp.edit();
		editor.putBoolean(DBString.SP_ACK_LOGON, ackLogon);
		editor.commit();

	}

	@Override
	public boolean isPersistedAckLogon() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean cleanClassified(boolean builtIn, boolean sync) {
		// TODO Auto-generated method stub
		// clean auth key
		cleanAuthenKey();
		// clean ack logon info
		cleanAckLogon();

		// sysManager.cleanIM(logout, sync);

		// callback UI synchronized
		if (sync) {
			for (ClassifiedListener listener : getAllListeners(null)) {
				listener.syncCleanBeforeLogon();
			}
		}

		return true;
	}

	@Override
	public boolean filterResponseReturnCode(int returnCode) {
		// TODO Auto-generated method stub
		if (returnCode == IProtocol.STATUS_CODE_SESSION_DUPLICATE_409) {
			for (ClassifiedListener listener : mListeners) {
				listener.finishLogoff(returnCode);
			}
			sessionDuplicate = true;
		} else if (returnCode == IProtocol.STATUS_CODE_SESSIOON_TIMEOUT_408) {
			if (sessionDuplicate || !autologon) {
				return false;
			}
			autologon = false;
			Log.d("Auto Logon", "Auto Logon");
			// TODO if it's good auto logon when session timeout?
			BuiltinService myInfo = BuiltinServiceManager.getInstance(mContext)
					.getBuitinService();
			taskLogon(null, myInfo.getUserName(), myInfo.getPassword(),
					myInfo.getPresence());
		}
		return false;
	}

	@Override
	public int taskRegister(ClassifiedListener listener, String username,
			String password, String phone) {
		TaskRegister task = new TaskRegister(listener, mCore, username,
				password, phone);
		put(listener, task.getClass().getName(),
				ClassifiedCore.PRIORITY_MEDIUM, task, true);

		return 0;
	}
	
	@Override
	public int taskForgotPassWord(ClassifiedListener listener, String email) {
		// TODO Auto-generated method stub
		TaskForgotPass task = new TaskForgotPass(listener, mCore, email);
		put(listener, task.getClass().getName(),
				ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;
	}

	@Override
	public int getUserProfileInfo( ClassifiedListener listener, String authenKey) {
		// TODO Auto-generated method stub
		TaskGetProfileInfo task = new TaskGetProfileInfo(listener,mCore, authenKey);

		put(listener, task.getClass().getName(),
				ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;

	}

	@Override
	public int updateUserProfile(Hashtable<String, String> infos,
			ClassifiedListener listener) {
		// TODO Auto-generated method stub
		// UpdateUserProfile task = new UpdateUserProfile(listener, this,
		// infos);
		// put(listener, task.getClass().getName(), IMCore.PRIORITY_MEDIUM,
		// task,
		// true);
		return 0;
	}

	@Override
	public int taskGetAndroidProductProposed(ClassifiedListener listeners,String AreaIds, String Keywords, String sortBy ,int pageLimit, int pageNumber,String CategoryType, String CategoryID) {
		TaskGetAndroidProductProposed task = new TaskGetAndroidProductProposed(listeners, mCore, "", "", pageLimit, pageNumber, CategoryType, CategoryID, AreaIds, Keywords, sortBy);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;
	}
	@Override
	public int taskGetProductSubCate(ClassifiedListener listeners,String hightLight, String GuaranteeStoreId, int CategoryId, int pageLimnit, String categoryType, String itemID, int pageNumber,int SortBy){
		TaskGetProductSubCate task = new TaskGetProductSubCate(listeners, mCore, hightLight, GuaranteeStoreId, CategoryId, pageLimnit, categoryType, itemID, pageNumber, SortBy);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;
	}
	@Override
	public int taskGetAndroidProductType(ClassifiedListener listeners,String CategoryID,String GuaranteeStoreID, String ItemId,int pageLimit, int pageNumber,String CategoryType,boolean isStore, String NotInPCodes) {
		TaskGetAndroidProductType task = new TaskGetAndroidProductType(listeners, mCore, CategoryID,GuaranteeStoreID, ItemId, pageNumber, pageLimit, CategoryType,isStore, NotInPCodes);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;
	}
	@Override
	public int taskGetAndroidProductSameCost(ClassifiedListener listeners,
			String CategoryID, String GuaranteeStoreID, String ItemId,
			int pageLimit, int pageNumber, String CategoryType,
			boolean isStore, String NotInPCodes, String sameCost) {
			TaskGetAndroidProductSameCost task= new TaskGetAndroidProductSameCost(listeners, mCore, CategoryID, GuaranteeStoreID, ItemId, pageLimit, pageNumber, CategoryType, isStore, NotInPCodes, sameCost);
			put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task, true);
			return 0;
	}
	@Override
	public int taskGetProductBySeller(ClassifiedListener listeners, String CategoryType, String CategoryId, String SellerName,String ItemId,int guaranteeStoreId,int pageNumber, int pageLimit, String NotInPCodes){
		TaskGetProductBySeller task = new TaskGetProductBySeller(listeners, mCore, CategoryType, CategoryId, SellerName, ItemId, guaranteeStoreId, pageNumber, pageLimit,NotInPCodes);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;
	}
	@Override
	public int taskGetCommentDetailProduct(ClassifiedListener listeners, String itemId){
		TaskCommentProduct task = new TaskCommentProduct(listeners, mCore, itemId);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task, true);
		return 0;
	}
	@Override
	public int taskGetAndroidInterest(ClassifiedListener listeners,String guaranteStoreId,String userName,String type){
		TaskGetAndroidInterestShop task = new TaskGetAndroidInterestShop(listeners, mCore, guaranteStoreId, userName, type);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);

		return 0;

	}
	@Override
	public int taskSendAndroidInterest(ClassifiedListener listeners,String guaranteeStoreId,String userName,int isInterest,String type, String authenkey){
		TaskSendAndroidInterestShop task = new TaskSendAndroidInterestShop(listeners, mCore, guaranteeStoreId, userName, isInterest,type, authenkey);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;

	}
	@Override
	public int taskComment(ClassifiedListener listeners, String itemId,String titleMsg, String message, String userName, int rating, String type,boolean isCommented,String timecomment) {
		TaskComment task = new TaskComment(listeners, mCore, itemId,titleMsg,message, userName, rating, type, isCommented, timecomment);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}
	
	@Override
	public int TaskSendAndroidProductAbusel(ClassifiedListener listeners, String ProductID, int UserID,String Message) {
		TaskSendAndroidProductAbusel task = new TaskSendAndroidProductAbusel(listeners, mCore, ProductID, UserID, Message);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}

	@Override
	public int taskPublish(JSONObject content, Vector<String> uploadPictures,
			ClassifiedListener listeners, String userName) {
		
		TaskPublish task = new TaskPublish(listeners, mCore, content, uploadPictures, userName);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);

		return 0;
	}
	@Override
	public int taskLogout(ClassifiedListener listener, String authenKey) {
		// TODO Auto-generated method stub
		TaskLogout task= new TaskLogout(listener, mCore, authenKey);
		put(listener, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}

	@Override
	public int taskGetMyHandbook(ClassifiedListener listeners, int type,
			String userName, int pageNumber, int pageLimit, String AuthKey,String UserId , String HisID) {
		// TODO Auto-generated method stub
		TaskGetMyHandBook task = new TaskGetMyHandBook(listeners, mCore, userName, AuthKey, type, pageNumber, pageLimit, UserId, HisID);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}
	@Override
	public int taskChangePass(ClassifiedListener listener,String oldPass, String newpassword,
			String authenKey) {
		// TODO Auto-generated method stub
		TaskChangePass task= new TaskChangePass(listener, mCore,oldPass, newpassword, authenKey);
		put(listener, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}

	@Override
	public int taskUpdateUserInfo(ClassifiedListener listener,
			String displayName, String mobile, String email, String area,
			String gender, String authenKey) {
		TaskUpdateUserInfo task= new TaskUpdateUserInfo(listener, mCore, displayName, mobile, email, gender, area, authenKey);
		put(listener, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}
	@Override
	public int taskUpdateViewProduct(ClassifiedListener listener,String PCode) {
		TaskUpdateViewProduct task = new TaskUpdateViewProduct(listener, mCore, PCode);
		put(listener, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}
	

	@Override
	public int taskChangeAvatar(ClassifiedListener listener,
			byte[] avatarchange, String authkey, String area) {
		// TODO Auto-generated method stub
		TaskChangeAvatar task = new TaskChangeAvatar(listener, mCore, avatarchange, authkey, area);
		put(listener, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}

	@Override
	public int taskGetSearchGlobal(ClassifiedListener listeners,
			String AreaIds, int IsDisable, String CategoryType, int pageLimit,
			int pageNumber, String Keywords,String CategoryId) {
		taskGetSearchGlobalProduct task = new taskGetSearchGlobalProduct(listeners, mCore, AreaIds, CategoryType, Keywords, IsDisable, pageLimit, pageNumber,CategoryId);
		put(listeners, task.getClass().getName(), ClassifiedCore.PRIORITY_MEDIUM, task,true);
		return 0;
	}

}
